package com.gobang.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.gobang.common.config.RabbitMQConfig;
import com.gobang.common.constant.BusinessErrorCode;
import com.gobang.common.exception.BusinessException;
import com.gobang.domain.dto.PageRequest;
import com.gobang.domain.dto.auth.LoginRequest;
import com.gobang.domain.dto.auth.RegisterRequest;
import com.gobang.domain.dto.mq.UserRegisteredMessage;
import com.gobang.domain.vo.PageData;
import com.gobang.domain.vo.auth.AuthResponse;
import com.gobang.domain.vo.auth.UserInfoVO;
import com.gobang.domain.vo.user.UserListItemVO;
import com.gobang.entity.PointsAccount;
import com.gobang.entity.User;
import com.gobang.entity.UserProfile;
import com.gobang.mapper.PointsAccountMapper;
import com.gobang.mapper.UserMapper;
import com.gobang.mapper.UserProfileMapper;
import com.gobang.service.UserService;
import com.gobang.util.JwtUtil;
import com.gobang.util.RedisUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import java.time.LocalDateTime;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.stream.Collectors;

/**
 * 用户服务实现类
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class UserServiceImpl implements UserService {

    private final UserMapper userMapper;
    private final UserProfileMapper userProfileMapper;
    private final PointsAccountMapper pointsAccountMapper;
    private final PasswordEncoder passwordEncoder;
    private final JwtUtil jwtUtil;
    private final RedisUtil redisUtil;
    private final RabbitTemplate rabbitTemplate;

    /**
     * 用户注册
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void register(RegisterRequest request) {
        // 1. 验证密码一致性
        if (!request.getPassword().equals(request.getConfirmPassword())) {
            throw new BusinessException(BusinessErrorCode.PASSWORD_CONFIRM_WRONG, "两次输入的密码不一致");
        }

        // 2. 检查账号是否已存在
        if (isUsernameExists(request.getUsername())) {
            throw new BusinessException(BusinessErrorCode.PARAM_ERROR, "账号已存在");
        }

        // 3. 创建用户账号
        User user = new User();
        user.setUsername(request.getUsername());
        user.setPassword(passwordEncoder.encode(request.getPassword()));
        user.setRole("ROLE_USER");
        user.setEnabled(true);

        int insertResult = userMapper.insert(user);
        if (insertResult <= 0) {
            throw new BusinessException(BusinessErrorCode.USER_REGISTER_FAIL, "用户注册失败");
        }

        // 4. 创建用户资料
        UserProfile userProfile = new UserProfile();
        userProfile.setUserId(user.getId());
        userProfile.setDisplayName(request.getDisplayName());
        userProfile.setTotalMatches(0);
        userProfile.setWinMatches(0);
        userProfile.setDrawMatches(0);
        userProfileMapper.insert(userProfile);

        // 5. 创建积分账户（初始积分为0）
        log.info("用户注册成功: userId={}, username={}", user.getId(), user.getUsername());

        UserRegisteredMessage registeredMessage = new UserRegisteredMessage();
        registeredMessage.setEventId(UUID.randomUUID().toString());
        registeredMessage.setUserId(user.getId());
        registeredMessage.setUsername(user.getUsername());
        registeredMessage.setOccurredAt(LocalDateTime.now());
        try {
            rabbitTemplate.convertAndSend(RabbitMQConfig.USER_REGISTER_QUEUE, registeredMessage);
        } catch (Exception ex) {
            log.warn("发送用户注册事件到MQ失败, userId={}, error={}", user.getId(), ex.getMessage(), ex);
            try {
                QueryWrapper<PointsAccount> wrapper = new QueryWrapper<>();
                wrapper.eq("user_id", user.getId());
                PointsAccount existing = pointsAccountMapper.selectOne(wrapper);
                if (existing == null) {
                    PointsAccount account = new PointsAccount();
                    account.setUserId(user.getId());
                    account.setCurrentPoints(0);
                    pointsAccountMapper.insert(account);
                    log.info("MQ发送失败，已在本地同步初始化积分账户, userId={}", user.getId());
                }
            } catch (Exception initEx) {
                log.error("MQ发送失败后本地初始化积分账户也失败, userId={}", user.getId(), initEx);
            }
        }
    }

    /**
     * 用户登录
     */
    @Override
    public AuthResponse login(LoginRequest request) {
        // 1. 查询用户
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("username", request.getUsername());
        User user = userMapper.selectOne(queryWrapper);

        if (user == null) {
            throw new BusinessException(BusinessErrorCode.USER_NOT_FOUND, "用户不存在");
        }

        // 2. 验证密码
        if (!passwordEncoder.matches(request.getPassword(), user.getPassword())) {
            throw new BusinessException(BusinessErrorCode.PASSWORD_WRONG, "密码错误");
        }

        // 3. 检查账号状态
        if (!user.getEnabled()) {
            throw new BusinessException(BusinessErrorCode.USER_DISABLED, "账号已被禁用");
        }

        // 4. 查询用户资料
        QueryWrapper<UserProfile> profileWrapper = new QueryWrapper<>();
        profileWrapper.eq("user_id", user.getId());
        UserProfile userProfile = userProfileMapper.selectOne(profileWrapper);

        // 5. 生成Token
        String token = jwtUtil.generateToken(user.getId(), user.getUsername(), user.getRole());

        // 6. 将Token存储到Redis中（比JWT过期时间稍长）
        jwtUtil.storeTokenInRedis(token, user.getId());

        // 7. 设置在线状态（TTL 30秒，后续由心跳续期）
        String onlineKey = "online:user:" + user.getId();
        redisUtil.set(onlineKey, "1", 30L);

        log.info("用户登录成功: userId={}, username={}", user.getId(), user.getUsername());

        return AuthResponse.builder()
                .token(token)
                .tokenType("Bearer")
                .userId(user.getId())
                .username(user.getUsername())
                .displayName(userProfile != null ? userProfile.getDisplayName() : user.getUsername())
                .role(user.getRole())
                .build();
    }

    /**
     * 检查账号是否存在
     */
    @Override
    public boolean isUsernameExists(String username) {
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("username", username);
        return userMapper.selectCount(queryWrapper) > 0;
    }

    @Override
    public void logout(String token) {
        if (!StringUtils.hasText(token)) {
            throw new BusinessException(BusinessErrorCode.TOKEN_MISSING, "Token缺失");
        }

        if (!jwtUtil.validateToken(token)) {
            throw new BusinessException(BusinessErrorCode.TOKEN_INVALID, "Token无效");
        }

        Long userId = jwtUtil.getUserIdFromToken(token);
        
        // 检查Token是否在Redis中存在，避免重复登出
        if (!jwtUtil.isTokenValidInRedis(token)) {
            log.info("Token在Redis中不存在，用户已登出: userId={}", userId);
            return;
        }
        
        // 从Redis中删除Token，实现登出
        jwtUtil.removeTokenFromRedis(token);
        log.info("从Redis删除Token成功: userId={}", userId);

        // 检查并清理在线状态
        if (userId != null) {
            String onlineKey = "online:user:" + userId;
            // 只有在线状态存在时才删除，避免不必要的Redis操作
            if (redisUtil.hasKey(onlineKey)) {
                redisUtil.delete(onlineKey);
                log.info("清理用户在线状态: userId={}", userId);
            }
        }

        log.info("用户登出成功: userId={}", userId);
    }

    /**
     *
     * @param pageRequest 分页参数
     * @param username    用户名（模糊查询，可为空）
     * @param role        角色（ROLE_USER/ROLE_ADMIN，可为空）
     * @param enabled     启用状态（可为空）
     * @return
     */
    @Override
    public PageData<UserListItemVO> pageUsers(PageRequest pageRequest, String username, String role, Boolean enabled) {
        Page<User> page = pageRequest.hasOrder() ? pageRequest.toPageWithOrder() : pageRequest.toPage();

        QueryWrapper<User> wrapper = new QueryWrapper<>();
        if (StringUtils.hasText(username)) {
            wrapper.like("username", username);
        }
        if (StringUtils.hasText(role)) {
            wrapper.eq("role", role);
        }
        if (enabled != null) {
            wrapper.eq("enabled", enabled);
        }

        Page<User> userPage = userMapper.selectPage(page, wrapper);

        if (userPage.getTotal() == 0) {
            return PageData.empty(pageRequest.getPageNum(), pageRequest.getPageSize());
        }

        List<User> users = userPage.getRecords();
        List<Long> userIds = users.stream()
                .map(User::getId)
                .collect(Collectors.toList());

        QueryWrapper<UserProfile> profileWrapper = new QueryWrapper<>();
        profileWrapper.in("user_id", userIds);
        List<UserProfile> profiles = userProfileMapper.selectList(profileWrapper);
        Map<Long, UserProfile> profileMap = profiles.stream()
                .collect(Collectors.toMap(UserProfile::getUserId, item -> item));

        List<UserListItemVO> voList = users.stream()
                .map(user -> {
                    UserProfile profile = profileMap.get(user.getId());
                    return UserListItemVO.builder()
                            .userId(user.getId())
                            .username(user.getUsername())
                            .displayName(profile != null ? profile.getDisplayName() : user.getUsername())
                            .role(user.getRole())
                            .enabled(user.getEnabled())
                            .createdAt(user.getCreatedAt())
                            .build();
                })
                .collect(Collectors.toList());

        return PageData.of(
                (int) userPage.getCurrent(),
                (int) userPage.getSize(),
                userPage.getTotal(),
                voList
        );
    }

    @Override
    public void updateUserStatus(Long userId, Boolean enabled) {
        if (enabled == null) {
            throw new BusinessException(BusinessErrorCode.PARAM_MISSING, "启用状态不能为空");
        }

        User user = userMapper.selectById(userId);
        if (user == null) {
            throw new BusinessException(BusinessErrorCode.USER_NOT_FOUND);
        }

        user.setEnabled(enabled);
        int rows = userMapper.updateById(user);
        if (rows <= 0) {
            throw new BusinessException(BusinessErrorCode.SYSTEM_ERROR, "账号状态更新失败");
        }

        if (!enabled) {
            String onlineKey = "online:user:" + userId;
            redisUtil.delete(onlineKey);
        }

        log.info("更新用户状态成功: userId={}, enabled={}", userId, enabled);
    }

    @Override
    public void updateUserRole(Long userId, String role) {
        if (!"ROLE_USER".equals(role) && !"ROLE_ADMIN".equals(role)) {
            throw new BusinessException(BusinessErrorCode.PARAM_INVALID, "角色取值不合法");
        }

        User user = userMapper.selectById(userId);
        if (user == null) {
            throw new BusinessException(BusinessErrorCode.USER_NOT_FOUND);
        }

        user.setRole(role);
        int rows = userMapper.updateById(user);
        if (rows <= 0) {
            throw new BusinessException(BusinessErrorCode.SYSTEM_ERROR, "角色更新失败");
        }

        log.info("更新用户角色成功: userId={}, role={}", userId, role);
    }
}

